#include "Client.h"
#include "Entity.h"

Entity::Entity(Client* cli){
	client = cli;
	memset(meta, 0, sizeof(meta));
	memset(name, 0, sizeof(name));
	location = Location();
	id = 0;
	living = false;
	flying = false;
	onGround = true;
	animSpeed = 0.0;
	animTick = 0.0;
	
	memset(&physicState, 0, sizeof(ge_RK4State));
	physicState.mass = 10.0;
	ResetForces();
	brake.x = brake.y = brake.z = 0.8;
	velocity.x = velocity.y = velocity.z = 0.0;
}

Entity::Entity(Client* cli, int _id){
	client = cli;
	memset(meta, 0, sizeof(meta));
	memset(name, 0, sizeof(name));
	location = Location();
	id = _id;
	living = false;
	flying = false;
	onGround = true;
	
	memset(&physicState, 0, sizeof(ge_RK4State));
	physicState.mass = 10.0;
	ResetForces();
	brake.x = brake.y = brake.z = 0.8;
	velocity.x = velocity.y = velocity.z = 0.0;
}

Entity::Entity(Client* cli, int _id, int _type){
	client = cli;
	memset(meta, 0, sizeof(meta));
	memset(name, 0, sizeof(name));
	location = Location();
	id = _id;
	type = _type;
	living = false;
	flying = false;
	onGround = true;
	
	memset(&physicState, 0, sizeof(ge_RK4State));
	physicState.mass = 10.0;
	ResetForces();
	brake.x = brake.y = brake.z = 0.8;
	velocity.x = velocity.y = velocity.z = 0.0;
}

void Entity::setMoved(bool m){
	moved = m;
}

bool Entity::hasMoved(){
	return moved;
}

void Entity::Update(double t, double dt){
	ge_RK4State earth = { { 0.0, 0.0, -6000000.0 }, { 0.0 }, { 0.0 }, 5.9736e24, 0.0, { 0.0 } }; // Earth
//	ge_RK4State earth = { { 0.0, 0.0, -1737000.0 }, { 0.0 }, { 0.0 }, 7.3477e22, 0.0, { 0.0 } }; // Moon
	ge_RK4State state2 = physicState;
//	ge_Vector3d orig_force = physicState.force;
//	ge_Vector3d force = physicState.force;
	ge_Vector3d force = { 0.0 };

	if(!flying){
		geRK4ApplyGravity(&state2, &earth);
		if(onGround){
			state2.force.z = 0.0;
		}
		force.z = state2.force.z;
	}

	geRK4ResetForce(&physicState);
	geRK4ApplyForce(&physicState, force);
	geRK4ApplyForce(&physicState, forces);
	
	Block* top = client->getWorld()->FindBlock(floor(physicState.position.x), floor(physicState.position.y), round(physicState.position.z) + 2.0);
	if(top && (top->getMaterial() && top->getMaterial()->isCollidable())){
		if(physicState.momentum.z > 0.0){
			physicState.momentum.z = 0.0;
		}
		if(forces.z > 0.0){
			forces.z = 0.0;
		}
	}
	
	physicState.momentum.x *= brake.x;
	physicState.momentum.y *= brake.y;
	physicState.momentum.z *= brake.z;
	geRK4Integrate(&physicState, t, dt);

	Block* block = client->getWorld()->FindBlock((int)floor(physicState.position.x), (int)floor(physicState.position.y), ((int)floor(physicState.position.z)));
	if(/*!block || */physicState.position.z > 0.0 && physicState.position.z < 256.0 && (block && (!block->getMaterial() || block->getMaterial()->isCollidable()))){
		onGround = true;
		flying = false;
		if(physicState.momentum.z < 0.0){
			physicState.momentum.z = 0.0;
			physicState.position.z = floor(physicState.position.z) + 1.0;
		}
	}else{
		onGround = false;
	}

	location.setX(physicState.position.x);
	location.setY(physicState.position.y);
	location.setZ(physicState.position.z);
}

void Entity::ResetForces(bool x, bool y, bool z){
	if(x)forces.x = 0.0;
	if(y)forces.y = 0.0;
	if(z)forces.z = 0.0;
}

void Entity::ApplyForce(double x, double y, double z){
	forces.x += x;
	forces.y += y;
	forces.z += z;
}

ge_Vector3d* Entity::getForces(){
	return &forces;
}

void Entity::SetBrakeFactor(double x, double y, double z){
	if(x > 0.0){
		brake.x = 1.0 / x;
	}
	if(y > 0.0){
		brake.y = 1.0 / y;
	}
	if(z > 0.0){
		brake.z = 1.0 / z;
	}
}

ge_RK4State* Entity::getPhysicState(){
	return &physicState;
}

void Entity::UpdateLocation(){
	double x = location.getX();
	double y = location.getY();
	location.Update();
	if(location.getDeltaT() != 0.0){
		velocity.x = (location.getX() - x) / location.getDeltaT();
		velocity.y = (location.getY() - y) / location.getDeltaT();
	}
}

int Entity::getId(){
	return id;
}

void Entity::setId(int _id){
	id = _id;
}

int Entity::getType(){
	return type;
}

void Entity::setType(int t){
	type = t;
}

bool Entity::isLiving(){
	return living;
}

bool Entity::isObject(){
	return !living;
}

void Entity::setLiving(bool l){
	living = l;
}

Location& Entity::getLocation(){
	return location;
}

void Entity::setLocation(Location loc){
	location = loc;

	physicState.position.x = loc.getX();
	physicState.position.y = loc.getY();
	physicState.position.z = loc.getZ();
}

void Entity::setVelocity(float vx, float vy, float vz){
	velocity.x = vx;
	velocity.y = vy;
	velocity.z = vz;

	physicState.momentum.x = velocity.x * physicState.mass;
	physicState.momentum.y = velocity.y * physicState.mass;
	physicState.momentum.z = velocity.z * physicState.mass;
}

ge_Vector3f Entity::getVelocity(){
	return velocity;
}

Metadata* Entity::getMetadata(){
	return meta;
}

bool Entity::isFlying(){
	return flying;
}

void Entity::setFlying(bool fly){
	flying = fly;
}

bool Entity::isOnGround(){
	return onGround;
}

void Entity::setOnGround(bool on){
	onGround = on;
}

char* Entity::getName(){
	return name;
}

void Entity::setName(char* s){
	strncpy(name, s, 64);
}

void Entity::Animate(float dt){
	animTick += dt * animSpeed;
}

float Entity::getAnimationSpeed(){
	return animSpeed;
}

void Entity::setAnimationSpeed(float s){
	animSpeed = s;
}

float Entity::getAnimationTick(){
	return animTick;
}
